package org.fin.framework.ioc.factory;

import org.fin.framework.ioc.annotation.*;
import org.fin.framework.ioc.beans.BeanDefinition;
import org.fin.framework.ioc.beans.BeanDefinitionReader;
import org.fin.framework.ioc.beans.DependencyDefinition;
import org.fin.framework.ioc.exception.IocException;
import org.fin.framework.ioc.exception.NoUniqueBeanDefinitionException;

import java.io.IOException;
import java.util.*;

public class AnnotationApplicationContext extends AbstractApplicationContext {

  private String scanPackage;

  public AnnotationApplicationContext(Class<?> configCls) {
    Objects.requireNonNull(configCls, "配置类不能为空");
    if (!configCls.isAnnotationPresent(ComponentScan.class)) {
      throw new IocException("警告：并未配置包扫描位置");
    }

    ComponentScan scan = configCls.getAnnotation(ComponentScan.class);
    scanPackage = scan.value();
    if (scanPackage.isBlank()) {
      scanPackage = configCls.getPackageName();
    }

    init();
  }

  @Override
  protected void loadBeanDefinitions() {
    // 这个负责扫描用户自定义的bean
    AnnotationScanner normalScanner = new AnnotationScanner(scanPackage);
    // 这个负责扫描框架本身需要的bean组件
    AnnotationScanner systemScanner = new AnnotationScanner("org.fin.framework.ioc.component");
    Class<?>[] supportedAnns = {Component.class, Service.class, Repository.class};

    try {
      // 扫描所有标识了注解的Bean，获取class对象集
      Set<Class<?>> results = normalScanner.scan(supportedAnns);
      results.addAll(systemScanner.scan(supportedAnns));


      // 遍历所有bean的class对象，并解析class标识的各种注解，创建BeanDefinition对象，注册到BeanFactory中
      Map<String, BeanDefinition> beanDefinitions = getBeanFactory().getBeanDefinitions();
      for (Class<?> beanCls : results) {
        // 创建BeanDefinitionReader，用来解析Bean里的注解，获取Bean的信息
        BeanDefinitionReader reader = new BeanDefinitionReader(beanCls);
        BeanDefinition bd = new BeanDefinition();

        // 解析Bean的基础信息
        parseBeanBaseInfo(bd, reader);

        // 如果有重复id的bean，则抛出错误
        if (beanDefinitions.containsKey(bd.getId())) {
          throw new NoUniqueBeanDefinitionException("bean id [" + bd.getId() + "] 重复，重复bean类型: [" + bd.getType().getName() + "], ["
              + beanDefinitions.get(bd.getId()).getType().getName()+ "]");
        }

        // 解析Bean的依赖信息
        parseBeanDependencyInfo(bd, reader);

        // 将解析完基本bean信息的BeanDefinition对象，注册到BeanFactory中
        registerBeanDefinition(bd);
      }

    }catch (IOException e) {
      throw new IocException("包扫描过程中出现未知异常", e);
    }


  }

  private void registerBeanDefinition(BeanDefinition bd) {
    getBeanFactory().getBeanDefinitions().put(bd.getId(), bd);
    getBeanFactory().getUninitializedBeanNames().add(bd.getId());

    // 判断这个bean的类型是否已经存在相同类型的bean，如果有，则放入同一个key里的bean id集合，
    // 否则，创建一个新的集合，放入类型和对应的bean id
    // 另外，bean的父类或者接口也被认为是bean的对应类型
    Map<Class<?>, Set<String>> beanNamesByType = getBeanFactory().getBeanNamesByType();

    List<Class<?>> classList = getAllSuperClassesAndInterfaces(bd.getType());
    classList.forEach(cls -> {
      if (beanNamesByType.containsKey(cls)) {
        Set<String> beanNames = beanNamesByType.get(cls);
        beanNames.add(bd.getId());
      }
      else {
        Set<String> set = new HashSet<>();
        set.add(bd.getId());
        beanNamesByType.put(cls, set);
      }
    });

  }

  private List<Class<?>> getAllSuperClassesAndInterfaces(Class<?> cls) {
    ArrayList<Class<?>> result = new ArrayList<>();

    while (cls != Object.class) {
      Class<?>[] interfaces = cls.getInterfaces();
      result.add(cls);
      result.addAll(Arrays.asList(interfaces));
      cls = cls.getSuperclass();
    }
    return result;
  }

  private void parseBeanDependencyInfo(BeanDefinition bd, BeanDefinitionReader reader) {
    List<DependencyDefinition> denpendentBeans = reader.getDependencyDefinition();
    bd.setDependencyDefinitions(denpendentBeans);
  }

  private void parseBeanBaseInfo(BeanDefinition bd, BeanDefinitionReader reader) {
    bd.setId(reader.getBeanId());
    bd.setType(reader.getBeanCls());
    bd.setAbstract(reader.isAbstract());
  }


}
